package com.bc.mes.config;


import com.bc.mes.service.SmsRoleService;
import com.bc.mes.service.SmsStaffService;
import com.bc.mes.bo.StaffDetails;
import com.bc.mes.component.JwtAuthenticationTokenFilter;
import com.bc.mes.component.RestAuthenticationEntryPoint;
import com.bc.mes.component.RestfulAccessDeniedHandler;


import com.bc.mes.mbg.model.smsstaff.SmsPermission;
import com.bc.mes.mbg.model.smsstaff.SmsStaff;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.List;


/**
* SpringSecurity的配置
*/
@Configuration// 配置类
@EnableWebSecurity// 激活security功能，默认已激活可以不配置
@EnableGlobalMethodSecurity(prePostEnabled=true) // 激活权限认证注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   @Autowired
   private SmsStaffService smsStaffService;

   @Autowired
   private SmsRoleService smsRoleService;
   @Autowired  // 权限不足 异常处理器
   private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
   @Autowired // 用户未认证时 处理器
   private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

   @Override // 配置security
   protected void configure(HttpSecurity httpSecurity) throws Exception {
       httpSecurity.csrf()// 由于使用的是JWT，我们这里不需要csrf
               .disable()
               .sessionManagement()// 基于token，所以不需要session
               .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
               .and()
               .authorizeRequests()
               .antMatchers(HttpMethod.GET, // 允许对于网站静态资源的无授权访问
                       "/",
                       "/*.html",
                       "/favicon.ico",
                       "/**/*.html",
                       "/**/*.css",
                       "/**/*.js",
                       "/swagger-resources/**",
                       "/v2/api-docs/**"
               )
               .permitAll()
               .antMatchers("/staff/login", "/staff/register")// 对登录注册要允许匿名访问
               .permitAll()
               .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求
               .permitAll()
               .antMatchers("/**")//测试时全部运行访问
               .permitAll()
               .anyRequest()// 除上面外的所有请求全部需要鉴权认证
               .authenticated();
       // 禁用缓存
       httpSecurity.headers().frameOptions().disable().cacheControl();

       // 添加JWT filter // 在security自带的的用户名密码 认证过滤器之前添加 jwt 认证过滤器
       httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);

       //添加自定义未授权和未登录结果返回
       httpSecurity.exceptionHandling()
               .accessDeniedHandler(restfulAccessDeniedHandler)// 权限不足异常处理
               .authenticationEntryPoint(restAuthenticationEntryPoint);// 认证不足异常处理
   }

   @Override // 可以省略不配置
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       auth.userDetailsService(userDetailsService())
               .passwordEncoder(passwordEncoder());
   }

   @Bean // 用户密码 编码 解码
   public PasswordEncoder passwordEncoder() {
       return new BCryptPasswordEncoder();
   }

   @Bean // 根据用户名获取 密码 权限 角色
   public UserDetailsService userDetailsService() {
       //获取登录用户信息
       return username -> {
           SmsStaff staff = smsStaffService.selectByUserName(username);//保证了User存在且状态不为0
           if (staff != null) {
               List<SmsPermission> permissionList = smsRoleService.getPermissionList(staff.getId());
               return new StaffDetails(staff,permissionList);
           }
           throw new UsernameNotFoundException("用户名或密码错误");
       };
   }

   @Bean // 在容器中注入 JwtAuthenticationTokenFilter
   public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
       return new JwtAuthenticationTokenFilter();
   }

   /**
    * 允许跨域调用的过滤器
    */
   @Bean
   public CorsFilter corsFilter() {
       UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
       CorsConfiguration config = new CorsConfiguration();
       config.addAllowedOrigin("*");
       config.setAllowCredentials(true);
       config.addAllowedHeader("*");
       config.addAllowedMethod("*");
       source.registerCorsConfiguration("/**", config);
       FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
       bean.setOrder(0);
       return new CorsFilter(source);
   }

   @Bean  //在容器中加入认证管理
   @Override
   public AuthenticationManager authenticationManagerBean() throws Exception {
       return super.authenticationManagerBean();
   }

}
